package com.neo.push.server;

import com.neo.push.core.codec.MessageDecoderHandler;
import com.neo.push.core.codec.MessageEncoderHandler;
import com.neo.push.core.connection.ConnectionFactory;
import com.neo.push.server.handler.ServerHearBeatHandler;
import com.neo.push.server.handler.ServerMessageHandler;
import com.neo.push.server.handler.TimeoutHandler;
import com.neo.push.server.listener.ServerListener;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Created by lock on 17-8-1.
 */
public class BasePushServer implements PushServer {

    private int port;
    private EventLoopGroup bossGroup;
    private EventLoopGroup workGroup;
    private ChannelFuture channelFuture;
    private ConnectionFactory connectionFactory;
    private Logger logger = LoggerFactory.getLogger(PushServer.class);
    private ExecutorService mainService = Executors.newSingleThreadExecutor();
    private List<ServerListener> serverListeners;

    @Override
    public void startServer(){
        mainService.execute(() -> {
            try{
                bossGroup = new NioEventLoopGroup(2);
                workGroup = new NioEventLoopGroup(4);
                ServerBootstrap serverBootstrap = new ServerBootstrap();
                serverBootstrap.group(bossGroup, workGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 128)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                socketChannel.pipeline()
                                        .addLast(new MessageDecoderHandler())
                                        .addLast(new MessageEncoderHandler())
                                        .addLast(new TimeoutHandler((int) TimeUnit.MINUTES.toMillis(4)))
                                        .addLast(new ServerHearBeatHandler())
                                        .addLast(new ServerMessageHandler(connectionFactory));
                            }
                        });
                channelFuture = serverBootstrap.bind(port).sync();
                logger.info(" push server started !");
                triggerPushServerStarted();
                channelFuture.channel().closeFuture().sync();
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                bossGroup.shutdownGracefully();
                workGroup.shutdownGracefully();
            }
        });
    }

    private void triggerPushServerStarted(){
        if(null!= serverListeners && serverListeners.size()>0){
            for(ServerListener listener : serverListeners){
                listener.serverStarted(this);
            }
        }
    }

    private void triggerPushServerClosed(){
        if(null!= serverListeners && serverListeners.size()>0){
            for(ServerListener listener : serverListeners){
                listener.serverClosed(this);
            }
        }
    }

    @Override
    public void stopServer() {
        channelFuture.channel().close();
        mainService.shutdownNow();
        logger.info(" push server closed !");
        triggerPushServerClosed();
    }

    public void setPort(int port) {
        this.port = port;
    }

    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public void setServerListeners(List<ServerListener> serverListeners) {
        this.serverListeners = serverListeners;
    }
}
